Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | const jwt = require('jsonwebtoken'); /** * JWT Service * Handles JWT token generation, verification, and cookie management */ class JwtService { constructor() { this.secret = process.env.JWT_SECRET || 'default-secret-change-me'; this.expiresIn = process.env.JWT_EXPIRES_IN || '1h'; this.cookieName = 'auth_token'; } /** * Generate JWT token * @param {Object} payload - { userId, email } * @returns {string} JWT token */ generateToken(payload) { return jwt.sign(payload, this.secret, { expiresIn: this.expiresIn }); } /** * Verify JWT token * @param {string} token - JWT token * @returns {Object} Decoded payload */ verifyToken(token) { try { return jwt.verify(token, this.secret); } catch (error) { throw new Error('Invalid or expired token'); } } /** * Extract token from Authorization header (Bearer token) * @param {string} authHeader - Authorization header value * @returns {string|null} Token or null */ extractTokenFromHeader(authHeader) { if (!authHeader) { return null; } const parts = authHeader.split(' '); if (parts.length !== 2 || parts[0] !== 'Bearer') { return null; } return parts[1]; } /** * Extract token from cookies * @param {Object} cookies - Request cookies object * @returns {string|null} Token or null */ extractTokenFromCookies(cookies) { if (!cookies || !cookies[this.cookieName]) { return null; } return cookies[this.cookieName]; } /** * Get cookie options for setting JWT cookie * @returns {Object} Cookie options */ getCookieOptions() { const isProduction = process.env.NODE_ENV === 'production'; return { httpOnly: true, // Prevents XSS attacks secure: isProduction, // HTTPS only in production sameSite: 'strict', // CSRF protection maxAge: this._getMaxAge(), path: '/' }; } /** * Get cookie max age in milliseconds * @private * @returns {number} */ _getMaxAge() { // Parse JWT_EXPIRES_IN (e.g., "1h", "7d") const expiresIn = this.expiresIn; if (expiresIn.endsWith('h')) { return parseInt(expiresIn) * 60 * 60 * 1000; } else if (expiresIn.endsWith('d')) { return parseInt(expiresIn) * 24 * 60 * 60 * 1000; } else if (expiresIn.endsWith('m')) { return parseInt(expiresIn) * 60 * 1000; } // Default: 1 hour return 60 * 60 * 1000; } /** * Get cookie name * @returns {string} */ getCookieName() { return this.cookieName; } } module.exports = JwtService; |